package cloud.xlh.infrastructure.utils;

import lombok.Data;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.AccessController;
import java.security.PrivilegedAction;

/**
 * Created with IntelliJ IDEA.
 *
 * @ project name : xlh_osms
 * @ Author: XuLeHuang
 * @ Date: 2023/7/3 10:16
 * @ Description: 大文件处理工具类，内存映射技术
 */
public class MmpUtil {
    //每次读写大小
    private static final long sizeForEveryTime = 11l;

    private static final StringBuilder stringBuilder = new StringBuilder("");

    public static void bigDataRead(BigFileHandle bigFileHandle , int readTimes) {
        long remainingSize = doBigDataRead(bigFileHandle , readTimes++);
        if (remainingSize != 0) {
            bigDataRead(bigFileHandle , readTimes);
        }
    }

    /**
     * @param readTimes
     * @return java.lang.Byte
     * @author XuLeHuang
     * @create 2023/7/3
     * @name:
     * @Description: 需要注意的是，在使用内存映射读取文件时，
     * 可能会遇到java.nio.file.InvalidPathException异常，这是由于文件路径格式不被支持或不合法引起的。
     * 可以使用java.nio.file.Paths.get()方法创建合法的路径。
     * 另外，在处理大文件时，可能需要考虑内存占用问题，
     * 可以使用FileChannel.MapMode.READ_WRITE模式创建可写入的内存映射缓冲区，避免将整个文件内容一次性加载到内存中。
     **/
    private static Long doBigDataRead(BigFileHandle bigFileHandle , int readTimes) {
        RandomAccessFile raf = null;
        FileChannel rafchannel = null;
        MappedByteBuffer map = null;
        try {
            raf = bigFileHandle.getRaf();
            rafchannel = bigFileHandle.getRafChannel();
            long hasRead = sizeForEveryTime * readTimes;
            long remainingSize = rafchannel.size() - hasRead;
            if (sizeForEveryTime < remainingSize) {
                //mmap  堆外  和文件映射的   byte  not  objtect
                map = rafchannel.map(FileChannel.MapMode.READ_WRITE, hasRead, sizeForEveryTime);
                bigDataConsolePrint(map.asReadOnlyBuffer());
                return remainingSize;
            } else {
                map = rafchannel.map(FileChannel.MapMode.PRIVATE, hasRead, remainingSize);  // 1000M的pageCache大小
                //randomAccessFile.close();//最后一次了
                bigDataConsolePrint(map.asReadOnlyBuffer());
                closeResources(raf, rafchannel, map);
                return 0l;
                //todo 资源关闭
            }
        } catch (IOException e) {
            e.printStackTrace();
            try {
                if (map != null) {
                    clean(map);
                }
                if (rafchannel != null) {
                    rafchannel.close();
                }
                if (raf != null) {
                    raf.close();
                }
            } catch (Exception e2) {
                throw new RuntimeException("随机文件对象和channel对象关闭失败");
            }
        }
        return null;
    }

    private static void closeResources(RandomAccessFile raf,
                                       FileChannel rafchannel,
                                       MappedByteBuffer map) {
        try {
            if (map != null) {
                clean(map);
            }
            if (rafchannel != null) {
                rafchannel.close();
            }
            if (raf != null) {
                raf.close();
            }
        } catch (Exception e) {
            throw new RuntimeException("随机文件对象和channel对象关闭失败");
        }
    }

    /**
     * @param buffer
     * @return void
     * @author XuLeHuang
     * @create 2023/7/3
     * @name:
     * @Description: https://www.cnblogs.com/rexienk/p/9324108.html
     **/
    public static void clean(final Object buffer) throws Exception {
        AccessController.doPrivileged(new PrivilegedAction() {
            public Object run() {
                try {
                    Method getCleanerMethod = buffer.getClass().getMethod("cleaner", new Class[0]);
                    getCleanerMethod.setAccessible(true);
                    sun.misc.Cleaner cleaner = (sun.misc.Cleaner) getCleanerMethod.invoke(buffer, new Object[0]);
                    cleaner.clean();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    public static void bigDataConsolePrint(ByteBuffer mappedByteBuffer) {
        if (mappedByteBuffer == null && mappedByteBuffer.limit() <= 0) {
            System.out.print("无数据输出......");
        }
        // 将缓冲区的内容读入ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(mappedByteBuffer.limit());
        buffer.put(mappedByteBuffer);
        buffer.flip();
        // 打印文件内容

        while (buffer.hasRemaining()) {
            char c = (char) buffer.get();
            System.out.print(c);
            stringBuilder.append(c);
            if (c==13) {
                //计数器佳1
                //如果计数器达到某个阈值，异步操作数据库
            }

        }
    }

   @Data
    static class BigFileHandle {
        private RandomAccessFile raf;
        private FileChannel rafChannel;
        private MappedByteBuffer map;

        public BigFileHandle(String filePath , String fileMode) {
            try {
                this.raf = new RandomAccessFile(filePath, fileMode);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            this.rafChannel = raf.getChannel();
        }
    }

    public static void main(String[] args) {
        BigFileHandle bigFileHandle = new BigFileHandle("D:\\bigDataWrite.txt" , "rw");
        bigDataRead(bigFileHandle , 0);
        String[] split = stringBuilder.toString().split("\r");
        for (int i = 0; i < split.length; i++) {
            System.out.println(split[i]);
        }
    }
}
